#include "private_implementations/JsonUtil.h"
#include <vector>
#include <iosfwd>
#include <cstdlib>

simframework::json::JSONUtil::JSONUtil(void)
{
}

simframework::json::JSONUtil::~JSONUtil(void)
{
}

SimWString simframework::json::JSONUtil::getKey(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt); // next char should be "
	startIt++; // skip "

	SimWString key;
	for (; startIt != endIt; ++startIt)
	{
		if (*startIt != '"')
			key += *startIt;
		else
		{
			startIt++;
			break;
		}
	}
	return key;
}

void simframework::json::JSONUtil::skipValue(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt == endIt) return;
	if (*startIt != ':')
	{
		throw 1;
		return;
	}
	startIt++; // skip :
	removeSpaces(startIt, endIt);

	switch(*startIt)
	{
	case '{':
		return JSONUtil::skipObjectOrArray(startIt, endIt);
		break;
	case '[':
		return JSONUtil::skipObjectOrArray(startIt, endIt);
		break;
	case '"':
		return JSONUtil::skipString(startIt, endIt);
		break;
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '-':
		return JSONUtil::skipNumber(startIt, endIt);
		break;
	case 'n':
		return JSONUtil::skipNull(startIt, endIt);
		break;
	case 't':
	case 'f':
		return JSONUtil::skipBoolean(startIt, endIt);
		break;
	default:
		break;
	}
}

void simframework::json::JSONUtil::skipString(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt == endIt) return;

	if (*startIt != '"')
	{
		throw 1;
		return;
	}

	startIt++; //skip "
	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '"')
		{
			startIt++;
			break;
		}
	}
}

void simframework::json::JSONUtil::skipNumber(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt == endIt) return;

	if (!(*startIt == '-' || isDigit(*startIt)))
	{
		throw 1;
		return;
	}
	bool valueFound = false;
	bool eFound = false;
	bool signFound = false;
	for (; startIt != endIt; ++startIt)
	{
		if (isDigit(*startIt)) {
			valueFound = true;
		} else if (valueFound && *startIt == '.') {
			continue;
		} else if (valueFound && !eFound && (*startIt == 'e' || *startIt == 'E')) {
			eFound = true;
			continue;
		} else if (valueFound && eFound && !signFound && (*startIt == '+' || *startIt == '-')) {
			signFound = true;
			continue;
		} else {
			break;
		}
	}
}

void simframework::json::JSONUtil::skipNull(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt == endIt) return;

	if (*startIt == 'n')
	{
		SimWString s = L"null";
		(void)checkPatternString(s, startIt, endIt);
	}
	else
	{
		throw 1;
	}
}

void simframework::json::JSONUtil::skipBoolean(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt == endIt) return;

	if (*startIt == 't')
	{
		SimWString s = L"true";
		(void)checkPatternString(s, startIt, endIt);
	}
	else if (*startIt == 'f')
	{
		SimWString s = L"false";
		(void)checkPatternString(s, startIt, endIt);
	}
	else
	{
		throw 1;
	}
}

void simframework::json::JSONUtil::skipObjectOrArray(SimWStringIter& startIt, SimWStringIter& endIt)
{
	std::vector<JSONType> stack;
	removeSpaces(startIt, endIt);
	if (startIt == endIt) return;

	switch (*startIt)
	{
	case '{':
		stack.push_back(JSON_OBJECT);
		break;
	case '[':
		stack.push_back(JSON_ARRAY);
		break;
	}
	startIt++; // skip { or [
	removeSpaces(startIt, endIt);

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top

		if (stack.empty())
		{
			startIt++;
			break;
		}
	}
}

void simframework::json::JSONUtil::removeSpaces(SimWStringIter& startIt, SimWStringIter& endIt)
{
	while (startIt != endIt && ::isspace(*startIt))
	{
		startIt++;
	}
}

void simframework::json::JSONUtil::cleanUp(SimWStringIter& startIt, SimWStringIter& endIt)
{
	removeSpaces(startIt, endIt);
	if (startIt != endIt && *startIt == ',')
	{
		startIt++;
		return;
	}
}

simframework::json::JsonObjectNode* simframework::json::JSONUtil::getObjectValue(SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	std::vector<JSONType> stack;
	removeSpaces(startIt, endIt);

	if (*startIt != '{')
	{
		if (status) *status = JSON_FAIL;
		return static_cast<JsonObjectNode*>(getDefault(JSON_OBJECT));
	}

	SimWString ws;
	bool valueFound = false;

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top
		wchar_t wc = *startIt;
		ws += wc;

		if (stack.empty())
		{
			valueFound = true;
			startIt++; // skip }
			break;
		}
	}
	if (valueFound == true)
	{
		if (status) *status = JSON_SUCCESS;
		return new JsonObjectNode(ws);
	}

	if (status) *status = JSON_FAIL;
	return static_cast<JsonObjectNode*>(getDefault(JSON_OBJECT));
}

simframework::json::JsonArrayNode* simframework::json::JSONUtil::getArrayValue(SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	std::vector<JSONType> stack;
	removeSpaces(startIt, endIt);

	if (*startIt != '[')
	{
		if (status) *status = JSON_FAIL;
		return static_cast<JsonArrayNode*>(getDefault(JSON_ARRAY));
	}

	SimWString ws;
	bool valueFound = false;

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '{') stack.push_back(JSON_OBJECT);
		else if (*startIt == '[') stack.push_back(JSON_ARRAY);
		else if (*startIt == '}') stack.pop_back(); // need to check top
		else if (*startIt == ']') stack.pop_back(); // need to check top
		wchar_t wc = *startIt;
		ws += wc;

		if (stack.empty())
		{
			valueFound = true;
			startIt++; // skip ]
			break;
		}
	}
	if (valueFound == true)
	{
		if (status) *status = JSON_SUCCESS;
		return new JsonArrayNode(ws);
	}

	if (status) *status = JSON_FAIL;
	return static_cast<JsonArrayNode*>(getDefault(JSON_ARRAY));
}

simframework::json::JSONString* simframework::json::JSONUtil::getStringValue(SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	removeSpaces(startIt, endIt);

	SimWString ws;
	startIt++; // skip "
	bool valueFound = false;

	for (; startIt != endIt; ++startIt)
	{
		if (*startIt == '\\')
		{
			if (checkPatternString(json_escape_quotation, startIt, endIt)) {
				ws += L'\x22';
			} else if (checkPatternString(json_escape_backslash, startIt, endIt)) {
				ws += L'\x5c';
			} else if (checkPatternString(json_escape_slash, startIt, endIt)) {
				ws += L'\x2f';
			} else if (checkPatternString(json_escape_backspace, startIt, endIt)) {
				ws += L'\x08';
			} else if (checkPatternString(json_escape_formfeed, startIt, endIt)) {
				ws += L'\x0c';
			} else if (checkPatternString(json_escape_newline, startIt, endIt)) {
				ws += L'\x0a';
			} else if (checkPatternString(json_escape_carriage, startIt, endIt)) {
				ws += L'\x0d';
			} else if (checkPatternString(json_escape_tab, startIt, endIt)) {
				ws += L'\x09';
			} else if (checkPatternString(json_escape_hexdigits, startIt, endIt)) {
				// read next two characters
				wchar_t wc1 = *startIt;	startIt++;
				wchar_t wc2 = *startIt;	startIt++;
				wchar_t x = (wc1<<8) | wc2;
				ws.append(1, x);

				if (startIt == endIt) break;
			}

			if (startIt == endIt)
			{
				valueFound = true;
				break;
			}
		}
		if (*startIt == '"')
		{
			valueFound = true;
			break;
		}
		else
		{
			wchar_t wc = *startIt;
			ws += wc;
		}
	}
	if (valueFound == true)
	{
		startIt++; // skip end "

		if (status) *status = JSON_SUCCESS;
		JSONString* data = new JSONString;
		data->strValue = ws;
		return data;
	}

	if (status) *status = JSON_FAIL;
	return static_cast<JSONString*>(getDefault(JSON_STRING));
}

simframework::json::JSONFloat* simframework::json::JSONUtil::getFloatValue(SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	removeSpaces(startIt, endIt);
	SimWString ws;
	bool valueFound = false;
	bool eFound = false;
	bool signFound = false;

	if (startIt != endIt && *startIt == '-')
	{
		wchar_t wc = *startIt;
		ws += wc;
	}
	for(; startIt != endIt; ++startIt)
	{
		if (isDigit(*startIt))
		{
			wchar_t wc = *startIt;
			ws += wc;
			valueFound = true;
		}
		else if (valueFound && *startIt == '.') // floating point
		{
			wchar_t wc = *startIt;
			ws += wc;
		}
		else if (valueFound && !eFound && (*startIt == 'e' || *startIt == 'E')) 
		{
			eFound = true;
			ws += *startIt;
		}
		else if (valueFound && eFound && !signFound && (*startIt == '+' || *startIt == '-')) 
		{
			signFound = true;
			ws += *startIt;
		}
		else
		{
			break;
		}
	}
	if (valueFound == true)
	{
		if (status) *status = JSON_SUCCESS;
		JSONFloat* data = new JSONFloat;
		data->fValue = _wtof(ws.c_str());
		return data;
	}

	if (status) *status = JSON_FAIL;
	return static_cast<JSONFloat*>(getDefault(JSON_FLOAT));
}

simframework::json::JSONInteger* simframework::json::JSONUtil::getIntValue(SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	removeSpaces(startIt, endIt);
	SimWString ws;
	bool valueFound = false;

	if (startIt != endIt && *startIt == '-')
	{
		wchar_t wc = *startIt;
		ws += wc;
	}

	for(; startIt != endIt; ++startIt)
	{
		if (isDigit(*startIt))
		{
			wchar_t wc = *startIt;
			ws += wc;
			valueFound = true;
		}
		else
		{
			break;
		}
	}
	if (valueFound == true)
	{
		if (status) *status = JSON_SUCCESS;
		JSONInteger* data = new JSONInteger;
		data->iValue = _wtoi(ws.c_str());
		return data;
	}
	
	if (status) *status = JSON_FAIL;
	return static_cast<JSONInteger*>(getDefault(JSON_INTEGER));
}

simframework::json::JSONBoolean* simframework::json::JSONUtil::getBooleanValue(SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	if (*startIt == 't') 
	{
		SimWString boolStr = L"true";
		bool res = checkPatternString(boolStr, startIt, endIt);
		if (res) 
		{
			if (status) *status = JSON_SUCCESS;
			JSONBoolean* data = new JSONBoolean;
			data->bValue = true;
			return data;
		}
	}
	else if (*startIt == 'f')
	{
		SimWString boolStr = L"false";
		bool res = checkPatternString(boolStr, startIt, endIt);
		if (res) 
		{
			if (status) *status = JSON_SUCCESS;
			JSONBoolean* data = new JSONBoolean;
			data->bValue = false;
			return data;
		}
	}
	
	if (status) *status = JSON_FAIL;
	return static_cast<JSONBoolean*>(getDefault(JSON_BOOLEAN));
}

bool simframework::json::JSONUtil::isDigit(wchar_t& c)
{
	switch(c)
	{
	case '0':
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
		{
			return true;
		}
	}
	return false;
}

bool simframework::json::JSONUtil::checkForNull(SimWStringIter& startIt, SimWStringIter& endIt)
{
	if (*startIt == 'n') 
	{
		SimWString nullStr = L"null";
		bool res = checkPatternString(nullStr, startIt, endIt);
		if (res) 
		{
			return true;
		}
	}
	return false;
}

bool simframework::json::JSONUtil::checkPatternString(SimWString& pattern, SimWStringIter& startIt, SimWStringIter& endIt)
{
	SimWStringIter itTmp = pattern.begin();
	SimWStringIter itTmpEnd = pattern.end();
	SimWStringIter it = startIt;

	bool found = true;
	unsigned int counter = 0;
	while (itTmp != itTmpEnd && it != endIt)
	{
		if (*it != *itTmp)
		{
			found = false;
		}
		itTmp++;
		it++;
		counter++;
	}
	if (found && counter == pattern.size())
	{
		// advance the original iterator
		for (unsigned int i=0; i < pattern.size(); ++i) startIt++;
		return true;
	}
	return false;
}

simframework::json::JsonObjectNode* simframework::json::JSONUtil::getObjectElementById(unsigned int elemId, unsigned int& start,
		SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipObjectOrArray(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getObjectValue(startIt, endIt, status);
}
simframework::json::JsonArrayNode* simframework::json::JSONUtil::getArrayElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipObjectOrArray(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getArrayValue(startIt, endIt, status);
}
simframework::json::JSONString* simframework::json::JSONUtil::getStringElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipString(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getStringValue(startIt, endIt, status);
}
simframework::json::JSONFloat* simframework::json::JSONUtil::getFloatElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipNumber(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getFloatValue(startIt, endIt, status);
}
simframework::json::JSONInteger* simframework::json::JSONUtil::getIntElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipNumber(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getIntValue(startIt, endIt, status);
}
simframework::json::JSONBoolean* simframework::json::JSONUtil::getBooleanElementById(unsigned int elemId, unsigned int& start, 
		SimWStringIter& startIt, SimWStringIter& endIt, int* status)
{
	for (unsigned int i=start; i < elemId; ++i,++start)
	{
		skipBoolean(startIt, endIt);
		cleanUp(startIt, endIt);
	}
	return getBooleanValue(startIt, endIt, status);
}

simframework::json::JsonAbstractNode* simframework::json::JSONUtil::getDefault(JSONType type)
{
	switch(type)
	{
	case JSON_OBJECT:	return &DEFAULT_JSON_OBJECT;
	case JSON_ARRAY:	return &DEFAULT_JSON_ARRAY;
	case JSON_STRING:	return &DEFAULT_JSON_STRING;
	case JSON_FLOAT:	return &DEFAULT_JSON_FLOAT;
	case JSON_INTEGER:	return &DEFAULT_JSON_INTEGER;
	case JSON_BOOLEAN:	return &DEFAULT_JSON_BOOLEAN;
	}
	return &DEFAULT_JSON_ABSTRACT; // should not reach here
}
